package com.ruoyi.common.utils;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSAUtil {

    private static final String algorithm = "RSA";

    static {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }

    /**
     * 随机生成密钥对
     * node-rsa
     * 生成 pkcs1 格式的密钥对
     * @param length
     * @return
     * @throws Exception
     */
    public static Map<String, String> genKeyPairNodeRsa(int length) throws Exception {
        Map<String,String> keyMap = new HashMap<>();
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGen.initialize(length);
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        String privateKey = null, publicKey = null;
        byte[] encoded = keyPair.getPrivate().getEncoded();
        PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(encoded);
        ASN1Encodable encodable = pkInfo.parsePrivateKey();
        ASN1Primitive primitive = encodable.toASN1Primitive();
        byte[] privateKeyPKCS1 = primitive.getEncoded();
        PemObject pemObject = new PemObject("RSA PRIVATE KEY", privateKeyPKCS1);
        try (StringWriter stringWriter = new StringWriter()) {
            try (PemWriter pemWriter = new PemWriter(stringWriter)) {
                pemWriter.writeObject(pemObject);
                pemWriter.flush();
                String pemString = stringWriter.toString();
                privateKey = pemString;
            }
        }

        encoded = keyPair.getPublic().getEncoded();
        SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(encoded);
        primitive = spkInfo.parsePublicKey();
        byte[] publicKeyPKCS1 = primitive.getEncoded();

        pemObject = new PemObject("RSA PUBLIC KEY", publicKeyPKCS1);
        try (StringWriter stringWriter = new StringWriter()) {
            try (PemWriter pemWriter = new PemWriter(stringWriter)) {
                pemWriter.writeObject(pemObject);
                pemWriter.flush();
                String pemString = stringWriter.toString();
                publicKey = pemString;
            }
        }
        //        // 将公钥和私钥保存到Map
        keyMap.put("public", publicKey);  //0表示公钥
        keyMap.put("private", privateKey);  //1表示私钥
        return keyMap;
    }


    /**
     * RSA私钥解密
     * node-rsa
     * @param str   加密字符串
     * @param privateKey  私钥
     * @return  铭文
     */
    public static String decryptNodeRsa(String str, String privateKey) throws Exception {
        String keyText = privateKey.split("-----")[2].replaceAll("\n", "").replaceAll("\r", "");
        byte[] bytes = Base64.decode(keyText.getBytes());
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(bytes);
        PrivateKey key = keyFactory.generatePrivate(privateKeySpec);

        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key);

        byte[] doFinal = cipher.doFinal(Base64.decode(str));
        return new String(doFinal, "utf-8");
    }




    /**
     * 随机生成密钥对
     * @param length
     * @return
     */
    public static Map<String, String> genKeyPair(int length) {
        Map<String,String> keyMap = new HashMap<>();
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 初始化密钥对生成器，密钥大小为96-1024位
        assert keyPairGen != null;
        keyPairGen.initialize(length, new SecureRandom());
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
        String publicKeyString = new String(org.apache.commons.codec.binary.Base64.encodeBase64(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyString = new String(org.apache.commons.codec.binary.Base64.encodeBase64((privateKey.getEncoded())));
        // 将公钥和私钥保存到Map
        keyMap.put("public", publicKeyString);  //0表示公钥
        keyMap.put("private", privateKeyString);  //1表示私钥
        return keyMap;
    }

    /**
     * RSA公钥加密
     * @param str  加密字符串
     * @param publicKey  公钥
     * @return  密文
     */
    public static String encrypt(String str, String publicKey) {
        //base64编码的公钥
        byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = null;
        String outStr = null;

        try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance(algorithm).generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            outStr = org.apache.commons.codec.binary.Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException |
                 NoSuchPaddingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //RSA加密
        return outStr;
    }

    /**
     * RSA私钥解密
     * @param str   加密字符串
     * @param privateKey  私钥
     * @return  铭文
     */
    public static String decrypt(String str, String privateKey) {
        //64位解码加密后的字符串
        byte[] inputByte = org.apache.commons.codec.binary.Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = null;
        //RSA解密
        Cipher cipher = null;
        String outStr = null;

        try {
            priKey = (RSAPrivateKey) KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(decoded));
            cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            outStr = new String(cipher.doFinal(inputByte));
        } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return outStr;
    }


    public static void main(String[] args) throws Exception {
        Map<String, String> keyMap = genKeyPair(512);
        String aPrivate = keyMap.get("private");
        String aPublic = keyMap.get("public");
        System.out.println("private = "+aPrivate);
        System.out.println("public = "+aPublic);

    }

}
